#import signal
import sys
import threading
#import time
#from collections import deque
#from memory import Memory
from interruptHandler import InterruptHandler
from cpu import CPU
from pcb import PCB
from timer import Timer
from aging import Aging



class Kernel:
    def __init__(self, aScheduler, memoryManager):
        #self.memory = Memory()
        self.pcbt = {}
        self.scheduler = aScheduler
        self.memoryManager=memoryManager
        self.memoryManager.setKernel(self)
        self.cpu = CPU(self, self.memoryManager)
        self.pid_counter = 0
        self.current_pcb = None
        self.lock = threading.RLock()
        self.running = False
        self.kernel_mode = None
        self.timer = None
        self.pcb = None  
        self.pid = None
        self.aging=None
        self.current_pid=None
        self.interrupt=InterruptHandler(self)
        

    def start(self):
        
        print('[kernel] starting...')
        if not self.running:
            self.running = True
            #self.cpu = CPU(self, self.memory)
            self.cpu.start()
            print('[kernel] started.')

        
    def stop(self):

        self.running = False
        sys.exit(0)





    def run(self, prog): # TODO: not synchronized
        self.lock.acquire()
        
        try:
            self.pid= self.pid_counter
            self.pid_counter = self.pid_counter + 1
            
            
            #self.pcb = PCB(self.pid,0,prog.priority)
            self.pcb = PCB(self.pid,0,prog.priority, prog.name, prog.programSize)
            #self.ready.append(pcb)
            self.addPcbToReady(self.pcb) #hacemos que refresque
            self.pcbt[self.pid] = self.pcb
            
            self.memoryManager.strategy.cargarPaginas(self.memoryManager, prog, self.pid)
            
            if self.isAging():
                self.aging.addPcbIntoTable(self.pcb)
        finally:
            self.lock.release()
        
        print('[kernel.run] pid %d: %s'%(self.pid, prog))
        
        self.interrupt.interrupt('new')


        
    def reschedule(self):
        print('[kernel.sch] %s'%(self.scheduler))
        print('[kernel.sch] %s'%(self.memoryManager.memory))
        
        if self.cpu.pc is not None: # salva el pcb si es necesario
            self.pcbt[self.current_pcb.pid] = self.cpu.pc  # cpu almacena pcbs en su pc
        
        if not self.scheduler.isEmpty():  # schedule next process
            
            self.current_pcb = self.scheduler.takeProgram() # le pide al planificador el siguiente programa a ejecutar              
            self.cpu.pc = self.current_pcb
            self.current_pid = self.current_pcb.pid
            
            print('[kernel.sch] scheduled pid %s'%(self.current_pid))
            print('[kernel.sch] %s'%(self.memoryManager.memory))
        else:
            self.set_idle()
            

    def inMemory(self, pid):
        
        return self.memoryManager.processInMemory(pid)

            
    def exitProgram(self):  #salida de un programa
        self.cpu.pc = None
        if self.isAging():
            self.aging.removePcbFromTable(self.pcbt[self.current_pid])
        del self.pcbt[self.current_pid]
        self.memoryManager.unload(self.current_pid)
        self.current_pid = None


        
    def io(self): # do nothing by now
        self.addPcbToReady(self.current_pcb)


        
    def idle(self):
        if self.current_pid is None:
            return True
        else:
            return False
    


    def set_idle(self):
        self.current_pid = None
        self.cpu.pc = None
        self.current_pcb = None


    def signal_handler(self, signal, frame):
        
        print("\n", "You pressed Ctrl+C!")
        
        self.running = False
        sys.exit(0)

    def initializeTimer(self, unNumero):
        self.timer = Timer(unNumero, self)
    
    def inicializeAging(self):
        self.aging = Aging(self)
        
    def addPcbToReady(self, aPcb):
        self.scheduler.addPcb(aPcb)
    
    def isAging(self):
        return self.aging is not None
    
